using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Reflection;
using System.Threading;

namespace CodeStory.SmartUpload
{
	public class SmartUploadHelper
	{
		HttpApplication application;
		readonly int BufferSize = 0x200000;
		public SmartUploadHelper(HttpApplication application) {
			this.application = application;
		}
		public void DoUpload() {
			HttpWorkerRequest workerRequest = (HttpWorkerRequest)((IServiceProvider)HttpContext.Current).GetService(typeof(HttpWorkerRequest));
			string contentType = workerRequest.GetKnownRequestHeader(HttpWorkerRequest.HeaderContentType).ToLower();
			if (contentType.IndexOf("multipart/form-data" )< 0) return;
			int contentLength = Convert.ToInt32(workerRequest.GetKnownRequestHeader(HttpWorkerRequest.HeaderContentLength));
			Encoding contentEncoding = application.Context.Request.ContentEncoding;
			SmartData smartdata = CreateSmartData(contentType, contentLength, contentEncoding);
			int dataReadedLength = 0;
			byte[] buffer = workerRequest.GetPreloadedEntityBody();
			smartdata.AppendBytes(buffer,0,buffer.Length);
			Thread thread = smartdata.StartParseThread();
			dataReadedLength += buffer.Length;
			if (!workerRequest.IsEntireEntityBodyIsPreloaded()) {
				while (dataReadedLength < contentLength) {
					if (!workerRequest.IsClientConnected()) {
						smartdata.Abort();
						break;
					} else {
						buffer = new byte[BufferSize];
						int read = workerRequest.ReadEntityBody(buffer, 0, BufferSize);
						smartdata.AppendBytes(buffer, 0, read);
						dataReadedLength += read;
					}
				}
			}
			thread.Join();
			SetUploadData(workerRequest,smartdata.SmartBuffer.GetAllBytes());
		}

		private SmartData CreateSmartData(string contentType, int contentLength, Encoding contentEncoding) {
			SmartData smartdata = new SmartData();
			smartdata.Application = application;
			smartdata.ContentLength = contentLength;
			smartdata.ContentEncoding = contentEncoding;
			smartdata.Boundary = Encoding.ASCII.GetBytes(("--" + contentType.Substring(contentType.IndexOf("boundary=") + 9)));
			smartdata.CurrentUploadContext.SmartProgress.Status = UploadStatus.Uploading;
			return smartdata;
		}
		private static void SetUploadData(HttpWorkerRequest worker, byte[] data) {
			BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance;
			Type type = worker.GetType();
			int dataLenght = data.Length;
			while (true) {
				if (((type == null) || (type.FullName == "System.Web.Hosting.ISAPIWorkerRequest")) || (type.FullName == "Microsoft.VisualStudio.WebHost.Request")) {
					break;
				}
				type = type.BaseType;
			}
			if (type == null) {
				//LoggingService.Error("Unable to set upload data.");
				return;
			}
			if (type.FullName == "System.Web.Hosting.ISAPIWorkerRequest") {
				try {
					type.GetField("_contentAvailLength", flags).SetValue(worker, dataLenght);
					type.GetField("_contentTotalLength", flags).SetValue(worker, dataLenght);
					type.GetField("_preloadedContent", flags).SetValue(worker, data);
					type.GetField("_preloadedContentRead", flags).SetValue(worker, true);
				} catch (Exception) {
					//LoggingService.Error("Unable to set upload data properties.");
					//LoggingService.Error(ex);
				}
			}
			if (type.FullName == "Microsoft.VisualStudio.WebHost.Request") {
				try {
					type.GetField("_contentLength", flags).SetValue(worker, dataLenght);
					type.GetField("_preloadedContent", flags).SetValue(worker, data);
					type.GetField("_preloadedContentLength", flags).SetValue(worker, dataLenght);
				} catch (Exception) {
					//LoggingService.Error("Unable to set debug upload data properties.");
					//LoggingService.Error(ex);
				}
			}
		}
	}
}
